package com.thiswhyme.fsplus.sentinel.feign.instancefilter;

import com.alibaba.csp.sentinel.context.Context;
import com.alibaba.csp.sentinel.node.DefaultNode;
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot;
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.thiswhyme.fsplus.utils.PatternUtils;
import org.springframework.util.CollectionUtils;

import java.util.Set;
import java.util.stream.Collectors;

/**
 * @description: 自定义熔断Slot，用于替换com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot
 * @author: thiswhyme
 * @date: 2021/4/30
 */
public class CustomDegradeSlot extends AbstractLinkedProcessorSlot<DefaultNode> {

    private BlockServerContext blockServerContext;

    public CustomDegradeSlot(BlockServerContext blockServerContext) {
        this.blockServerContext = blockServerContext;
    }

    @Override
    public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args)
            throws Throwable {
        if(PatternUtils.isIpPort(resourceWrapper.getName())) {
            //实例级别的资源，走自定义断路校验逻辑
            checkInstanceDegrade(resourceWrapper, context, node, count);
        } else {
            //其他资源，走原断路校验逻辑
            DegradeRuleManager.checkDegrade(resourceWrapper, context, node, count);
        }
        fireEntry(context, resourceWrapper, node, count, prioritized, args);
    }

    /**
     * 校验实例熔断
     * 若发生熔断，将该实例添加到“不可用实例”容器中，到达窗口期后再从容器剔除
     * @param resource
     * @param context
     * @param node
     * @param count
     */
    public void checkInstanceDegrade(ResourceWrapper resource, Context context, DefaultNode node, int count) {
        Set<DegradeRule> rules = DegradeRuleManager.getRules().stream().filter(rule -> resource.getName().equals(rule.getResource())).collect(Collectors.toSet());;
        if (CollectionUtils.isEmpty(rules)) {
            return;
        }
        for (DegradeRule rule : rules) {
            if (!rule.passCheck(context, node, count)) {
                blockServerContext.add(resource.getName(), System.currentTimeMillis() + rule.getTimeWindow() * 1000);
            }
        }
    }

    @Override
    public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
        fireExit(context, resourceWrapper, count, args);
    }
}
